package com.rongji.dfish.demo.editor.service;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.Id;

import org.springframework.core.annotation.AnnotationUtils;

import com.rongji.dfish.base.Utils;
import com.rongji.dfish.demo.constant.DemoConstants;
import com.rongji.dfish.framework.IdGenerator;
import com.rongji.dfish.framework.service.BaseService;

public class CommonService<T> extends BaseService<T, String> {

	/**
	 * 校验对象,比如校验对象的主键编号是否存在,不存在时自动设置编号等;
	 * 这个方法仅在{@link #save(Object)}和 {@link #update(Object)}时调用,如不需要重写此方法,实现为空即可
	 * @param object
	 * @param checkPk
	 */
	protected void checkEntity(Object object, boolean checkPk) {
		// do something
		if (checkPk) {
			Class<?> objClass = object.getClass();
			String pk = getPrimaryKey(objClass);
			if (Utils.notEmpty(pk)) {
				try {
					String suffix = pk.substring(0, 1).toUpperCase() + pk.substring(1);
					String setter = "set" + suffix;
					String getter = "get" + suffix;
					Method getMethod = objClass.getMethod(getter, new Class[]{});
					Object value = getMethod.invoke(object, new Object[]{});
					if (value == null || "".equals(value)) {
						Method setMethod = objClass.getMethod(setter, new Class[]{ getMethod.getReturnType() });
						value = getNewId();
						setMethod.invoke(object, new Object[]{ value });
					}
				} catch (SecurityException e) {
					e.printStackTrace();
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static String getNewId() {
		return IdGenerator.getSortedId();
	}
	
	public void save(Object object) {
		if (object == null) {
			return;
		}
		checkEntity(object, true);
		pubCommonDAO.save(object);
	}
	
	private String getPrimaryKey(Class<?> objClass) {
		// FIXME 未使用注解的主键无法获取
		// 当前类或者继承父类的注解
		if (AnnotationUtils.isAnnotationDeclaredLocally(Entity.class, objClass) ||
				AnnotationUtils.isAnnotationInherited(Entity.class, objClass)) {
			Method[] methods = objClass.getDeclaredMethods();
			for (Method method : methods) {
				if (method.isAnnotationPresent(Id.class)) {
					String methodName = method.getName();
					if (methodName.length() > 3) {
						// Hibernate默认产生的Id注解在getter上
						String fieldName = methodName.substring(3);
						return fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
					} else {
						// 容错处理,理论上默认就是getter方法
						return methodName;
					}
				}
			}
			Field[] fields = objClass.getDeclaredFields();
			for (Field field : fields) {
				// FIXME 目前仅处理单主键的情况,复合主键暂不处理
				if (field.isAnnotationPresent(Id.class)) {
					return field.getName();
				}
			}
		}
		return "";
	}
	
	public void update(Object object) {
		checkEntity(object, false);
		pubCommonDAO.update(object);
	}
	
	public void delete(Object object) {
		pubCommonDAO.delete(object);
	}
	
	public <O extends Object> O getOne(Class<O> clz, Object pkValue) {
		if (clz == null || pkValue == null || "".equals(pkValue)) {
			return null;
		}
		String pk = getPrimaryKey(clz);
		if (Utils.notEmpty(pk)) {
			Entity entity = clz.getAnnotation(Entity.class);
			String tableName = "";
			if (entity != null) {
				tableName = entity.name();
			}
			if (Utils.isEmpty(tableName)) {
				tableName = clz.getSimpleName();
			}
			@SuppressWarnings("unchecked")
            List<O> result = (List<O>) pubCommonDAO.getQueryList("FROM " + tableName + " t WHERE t." + pk + "=?", pkValue);
			if (Utils.notEmpty(result)) {
				return result.get(0);
			}
		}
		return null;
	}
	
	/**
     * 
     * @param data Object[] 0:编号(String),1:排序号(Long)
     * @param key
     * @param moveType
     * @return
     */
    protected List<Object[]> getData4Update(List<Object[]> data, String key, String moveType) {
    	if (Utils.isEmpty(data) || Utils.isEmpty(key) || Utils.isEmpty(moveType)) {
    		return Collections.emptyList();
    	}
    	// 修复旧数据顺序的排序号
    	List<Object[]> fixedData = new ArrayList<Object[]>();
    	// 数据量
    	int dataCount = 0;
    	// 移动数据所处位置
    	int index = -1;
    	for (Object[] d : data) {
    		if (index < 0 && key.equals(d[0])) {
    			index = dataCount;
    		}
    		fixedData.add(new Object[]{ d[0], 10L * (++dataCount) });
    	}
    	if (index < 0) {
    		return Collections.emptyList();
    	}
    	if (DemoConstants.MOVE_TOP.equals(moveType)) { // 置顶
    		if (index > 0) {
    			fixedData.get(index)[1] = 10L;
    			for (int i=0;i<index; i++) {
    				fixedData.get(i)[1] = (Long)fixedData.get(i)[1] + 10L;
    			}
    		}
    	} else if (DemoConstants.MOVE_UP.equals(moveType)) { // 上移
    		if (index > 0) {
    			exchangeOrder(fixedData.get(index), fixedData.get(index - 1));
    		}
    	} else if (DemoConstants.MOVE_DOWN.equals(moveType)) { // 下移
    		if (index < dataCount - 1) {
    			exchangeOrder(fixedData.get(index), fixedData.get(index + 1));
    		}
    	} else if (DemoConstants.MOVE_BOTTOM.equals(moveType)) { // 置底
    		if (index < dataCount - 1) {
    			fixedData.get(index)[1] = 10L * dataCount;
    			for (int i=dataCount-1; i>index; i--) {
    				fixedData.get(i)[1] = (Long)fixedData.get(i)[1] - 10L;
    			}
    		}
    	}
    	
    	Comparator<Object[]> comparator = new Comparator<Object[]>() {
			public int compare(Object[] o1, Object[] o2) {
				return ((String) o1[0]).compareTo((String) o2[0]);
			}
		};
		Collections.sort(data, comparator);
		Collections.sort(fixedData, comparator);
		List<Object[]> updateData = new ArrayList<Object[]>();
		for (int i=0; i<dataCount; i++) {
			Object[] origin = data.get(i);
			Object[] fixed = fixedData.get(i);
			if (!origin[1].equals(fixed[1])) {
				updateData.add(fixed);
			}
		}
    	return updateData;
    }
    
    private void exchangeOrder(Object[] data1, Object[] data2) {
    	Object temp = data1[1];
    	data1[1] = data2[1];
    	data2[1] = temp;
    }
	
}
